#include "chess.h"

#include <cstdlib>

using namespace std;

MoveGenerator ChessBoard::myGen;//BUG: causes memory leak (still reachable, from bbtk::instance()), not freed on exit
//as this only happens once, not a big issue, exactly 131,584 bytes "still reachable" in 1 block, was quick fix to be compatible with chess package
//was originally a member variable of an engine class, and all user code needed to be in a function of the engine class
//additionally, there was no functionality in the original package to deallocate the memory

const char MAX_MOVES_NO_PROGRESS = 100;

//BEGIN ADDITIONAL FUNCTIONS

//END ADDITIONAL FUNCTIONS

/*BEGIN LINE NUMBERS OF CHANGED CODE BELOW

Example:
getMoves():35-39:added preallocation heuristics for return vector

BEGIN LINE NUMBERS OF CHANGED CODE IN OTHER PROVIDED FILES

Example:
MoveGenerator.h:MoveGenerator():48-52:fixed handling of static class instance to deallocate via reference counting

END LINE NUMBERS*/

void ChessBoard::getCastles(bool mycastles[4])
{//could do this in a loop by noting the actual values of the #defines, but hey, its just 4 lines
	mycastles[0] = (myboard.flags & whiteLongCastleMask) != 0;
	mycastles[1] = (myboard.flags & whiteShortCastleMask) != 0;
	mycastles[2] = (myboard.flags & blackLongCastleMask) != 0;
	mycastles[3] = (myboard.flags & blackShortCastleMask) != 0;
}

unsigned char ChessBoard::getPiece(char row, char col)
{//see Position.h for return values, use EMPTY and B_ or W_ and piece name, eg, B_KING
	return myboard.pieceAt(row * 8 + (7 - col));
}

short ChessBoard::getMoves(Move* &moves)
{
	if (!legalNextMoves)//don't generate more than once for a single state
	{
		std::vector<Move>* nextMoves=myGen.generateMoves(&myboard);
		legalNextMoves = new std::vector<Move>;//could preallocate
		std::vector<Move>::iterator iter;
		for(iter=nextMoves->begin(); iter<nextMoves->end(); iter++)
		{
			myboard.doMove(*iter);
			if(myGen.isLegal(&myboard))
			{
				legalNextMoves->push_back(*iter);
			}
			myboard.undoMove(*iter);
		}
		delete nextMoves;
	}
	short end = (short)legalNextMoves->size(), i = 0;
	moves = new Move[end];
	vector<Move>::iterator iter_end = legalNextMoves->end();
	for (vector<Move>::iterator iter = legalNextMoves->begin(); iter != iter_end; ++iter)
	{
		moves[i] = *iter;
		++i;
	}
	return end;
}

bool ChessBoard::minPlayerTurn()
{
	return (!(myboard.flags & playerMask));
}

bool ChessBoard::inCheck()
{
	if (myboard.flags & playerMask)
	{//white to move
		return 0 != (~(myboard.w_pawn | myboard.w_knight | myboard.w_bishop | myboard.w_rook | myboard.w_queen) & myboard.w_occupied & myGen.getAttacks(&myboard, false));
	} else {//black to move
		return 0 != (~(myboard.b_pawn | myboard.b_knight | myboard.b_bishop | myboard.b_rook | myboard.b_queen) & myboard.b_occupied & myGen.getAttacks(&myboard, true));
	}
}

bool ChessBoard::gameOver(bool &draw, bool& minPlayerWin)
{
	bool repeat = true;
	for (short i = 0; i < 5; ++i)
	{
		if (history[i] != history[i + 4])
		{
			repeat = false;
			break;
		}
	}
	if (repeat)
	{
		draw = true;
		return true;
	}
	if (!legalNextMoves)
	{
		std::vector<Move>* nextMoves=myGen.generateMoves(&myboard);
		legalNextMoves = new std::vector<Move>;
		std::vector<Move>::iterator iter;
		for(iter=nextMoves->begin(); iter<nextMoves->end(); iter++)
		{
			myboard.doMove(*iter);
			if(myGen.isLegal(&myboard))
			{
				legalNextMoves->push_back(*iter);
			}
			myboard.undoMove(*iter);
		}
		delete nextMoves;
	}
	if (legalNextMoves->size() == 0)
	{
		if (myboard.flags & playerMask)
		{
			if (~(myboard.w_pawn | myboard.w_knight | myboard.w_bishop | myboard.w_rook | myboard.w_queen) & myboard.w_occupied & myGen.getAttacks(&myboard, false))
			{
				draw = false;
				minPlayerWin = true;
				return true;
			}
		} else {
			if (~(myboard.b_pawn | myboard.b_knight | myboard.b_bishop | myboard.b_rook | myboard.b_queen) & myboard.b_occupied & myGen.getAttacks(&myboard, true))
			{
				draw = false;
				minPlayerWin = false;
				return true;
			}
		}
		draw = true;
		return true;
	}
	if (!movesToDraw)
	{
		draw = true;
		return true;
	}
	return false;
}

ChessBoard* ChessBoard::move(Move* move)
{
	ChessBoard* ret = new ChessBoard(this);
	for (short i = 8; i > 0; --i) ret->history[i] = history[i - 1];//update history
	ret->history[0] = move->move;
	ret->myboard.doMove(*move);
	if ((myboard.pieceAt(move->from()) == W_PAWN) || (myboard.pieceAt(move->from()) == B_PAWN) || move->isCapture() || move->isEnPassantCapture() || move->isPromotion() || move->breaksBlackKingCastle() || move->breaksBlackQueenCastle() || move->breaksWhiteKingCastle() || move->breaksWhiteQueenCastle())
	{
		ret->movesToDraw = MAX_MOVES_NO_PROGRESS;
	} else ret->movesToDraw = movesToDraw - 1;
	return (ChessBoard*)ret;
}

void ChessBoard::freeMoves(Move* moves)
{
	delete[] moves;
}

ChessBoard::~ChessBoard()
{
	myboard.destroy();
	if (legalNextMoves)
	{
		legalNextMoves->clear();
		delete legalNextMoves;
	}
}

ChessBoard::ChessBoard()
{
	myboard = Position("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
	movesToDraw = MAX_MOVES_NO_PROGRESS;
	legalNextMoves = NULL;//moves not generated unless called for
	for (short i = 0; i < 9; ++i) history[i] = i;//make sure history doesnt say automatic threefold repetition, set all moves in history to be different invalid moves
}

ChessBoard::ChessBoard(ChessBoard* tocopy)
{
	for (short i = 0; i < 9; ++i) history[i] = tocopy->history[i];
	myboard = tocopy->myboard;
	legalNextMoves = NULL;
	movesToDraw = tocopy->movesToDraw;
}

ChessBoard::ChessBoard(const ChessBoard& tocopy)
{
	for (short i = 0; i < 9; ++i) history[i] = tocopy.history[i];
	myboard = tocopy.myboard;
	if (tocopy.legalNextMoves == NULL)
	{
		legalNextMoves = NULL;
	} else {
		legalNextMoves = new vector<Move>();
		*legalNextMoves = *(tocopy.legalNextMoves);
	}
	movesToDraw = tocopy.movesToDraw;
}

ChessBoard& ChessBoard::operator=(const ChessBoard& tocopy)
{
	for (short i = 0; i < 9; ++i) history[i] = tocopy.history[i];
	myboard = tocopy.myboard;
	if (tocopy.legalNextMoves == NULL)
	{
		legalNextMoves = NULL;
	} else {
		legalNextMoves = new vector<Move>();
		*legalNextMoves = *(tocopy.legalNextMoves);
	}
	movesToDraw = tocopy.movesToDraw;
	return *this;
}

void ChessBoard::printState()
{
	myboard.displayChessboard();
}


char* ChessBoard::getFen()
{
  static char buffer[256];
  const char castles[] = "QKqk";
  
  
  memset(buffer, 0, 256);
  char* p = buffer;
      
  for(int i = 0; i < 64; i++)
  {
    if(i > 0 && i % 8 == 0)
    {
      *p = '/';
      p++;
    }
    int piece = getPiece(i / 8, i % 8);
    if(piece == B_PAWN)
    {
      *p = 'p';
      p++;
    }
    else if(piece == B_KNIGHT)
    {
      *p = 'n';
      p++;
    }
    else if(piece == B_BISHOP)
    {
      *p = 'b';
      p++;
    }
    else if(piece == B_ROOK)
    {
      *p = 'r';
      p++;
    }
    else if(piece == B_QUEEN)
    {
      *p = 'q';
      p++;
    }
    else if(piece == B_KING)
    {
      *p = 'k';
      p++;
    }
    else if(piece == W_PAWN)
    {
      *p = 'P';
      p++;
    }
    else if(piece == W_KNIGHT)
    {
      *p = 'N';
      p++;
    }
    else if(piece == W_BISHOP)
    {
      *p = 'B';
      p++;
    }
    else if(piece == W_ROOK)
    {
      *p = 'R';
      p++;
    }
    else if(piece == W_QUEEN)
    {
      *p = 'Q';
      p++;
    }
    else if(piece == W_KING)
    {
      *p = 'K';
      p++;
    }
    else
    {
      *p = '1';
      p++;
    }
  }
  *p = ' ';
  p++;
  if(minPlayerTurn())
    *p = 'b';
  else
    *p = 'w';
  p++;
  *p = ' ';
  p++;
  
  bool localCastles[4];
  getCastles(localCastles);
  for(int i = 0; i < 4; i++)
  {
    if(localCastles[i])
    {
      *p = castles[i];
      p++;
    }
  }
  *p = ' ';
  p++;
  
  int ep = myboard.getEnPassantStatus();
  if(ep)
  {
    *p = 'h' - (ep % 8);
    p++;
    if(ep/8 == 1) *p = '6';
    else *p = '3';
    p++;
  }
  else
  {
    *p = '-';
    p++;
  }
  
  /*
  sprintf(p, "%d", 50 - movesToDraw);
  for( ; *p; p++) ; //get to the end of p.
  sprintf(p, "%d", turn);*/
  return buffer;
}